<?php
/*
* Kusht Mud.
* ----
* The MIT License
* Copyright (c) 2010 Andrew Tutt
* ----
*/

/* StateMngr
*
* The StateMngr handles all state changes in the game and processes them
* by broadcasting the intention to change the state to registered listeners
* and allows them to interrupt or intercede execution.
*
* Thus, this class handles all game scripting as scripts are written separately
* from objects and bound individually. Timed scripts can also be registered globally
* so that they execute on intervals.
*/

class Listener extends saveable { // See 'BasicStructs' For the defintion of "saveable"

public $script; 	// This is the executable code
public $localVar;   // Allows the Script to Store Something Between Executions

// Mostly for the database
public $a_object;
public $trigr;

// When attached to an object, it intercedes:
// + Statechanges
// + Commands Issued Within (Room)
// + Commands Issued Near (Room)
// + Callable on Timers if Bound to TIME
function attach($trigger, $object) {
	$this->trigr 	= $trigger;
	$this->a_object = $object;
	StateMngr::$Listeners += array($this->trigr=>array($object->copy_id=>$this));
}

function detach() {
	unset(StateMngr::$Listeners[$this->trigr][$this->a_object->copy_id]);
}

function do_execute($user = NULL, $args = NULL) {
	$Fun = create_function('$listener,$free,$player,$room,$object,$args',$this->script);
	return $Fun($this,$this->localVar,$user,$user->in_room(),$this->a_object,$args);
}

}

class StateMngr {

public static $Listeners = array(); // as (State || Flag || Cmd)=>array(Listeners)

public static function handleTimedListeners($time) {
	if(!isset(StateMngr::$Listeners[TIME])) { return; }

	$relevant_listeners = StateMngr::$Listeners[TIME]; // The Listeners bound to TIME
	foreach($relevant_listeners as $r) {
		$r->do_execute($time);
	}
}

public static function handleCmd($user,$cmd,$phr) {
	/* Quite Simply, something must register interest in the specific command Word
	   and simultaneously have the geographical capacity to be influenced by it.
	   Take, for instance the "Follow" Command. "Follow" is interceded if a player is
	   charmed. In fact, many, many commands are interceded by Charm. Though it is a bit of trouble,
	   we register the charm spell with an interceding script, saying that if a character is affected
	   by charm, then register local interest in the charm spell for that user and override the command
	   if issued. 
	*/
	
	// Find the Command as Best we are able
	$trigger = $cmd;
	
	// What command would the game think it is?
	foreach($user->in_room()->exits as $exit_) {
		if (strpmatch($exit_->command,$trigger)) {
			$trigger = $exit_->command;
			break;
		}
	}
	
	foreach(Commands::$CMDLIST as $fullword) {
		if (strpmatch($fullword,$trigger)) {
			$trigger = $fullword;
			break;
		}
	}
		
	// Now we go through everything...
		// For room
		// For each object in room
		// For each user in room
		// For each object in inventory
		// Broadcast the Command
	$L = StateMngr::getRelevantListeners($user,$trigger);
	return StateMngr::Broadcast($L,$user,$phr);

	// Right now, we'll hardcode it, but it will be easy to revise these to scripts
	if($user->is(FORCED_SLEEP)) {
		if (strpmatch("stand",$cmd)	||
			strpmatch("wake",$cmd)	||
			strpmatch("sit",$cmd)	||
			strpmatch("rest",$cmd)) {
			$object->send("You can't wake up!");
			return true;
		}
	}
	
	return false;
}
	
// Do a similar check
// Return the result to let the state change happen or not. Send output to each of the users
// and potentially call a host of other built-in functions. Happens when entering/leaving
// rooms, setting flags, etc.
public static function handleStateChange($object,$prop_name,$property) {
	// TODO: Trigger Affect or Listener and ask for a false return
	// IF no false returned, continue with execution...
	
	$object->$prop_name = $property; // Make the change
	clientHandler::handleStateChange($object,$prop_name,$property);	// Tell ClientHandler
	return true;
}

// Choose whether to allow a flag to change state
public static function handleFlagChange($object,$flag,$turning_on) {
	if($turning_on) {
		SET_BIT($object->all_flags,$flag);
		// Tell the ClientHandler This Too
	} else {
		RMV_BIT($object->all_flags,$flag);
		// Tell the ClientHandler Too
	}
}

// Does this even belong here???
// $user is querying object
public static function handleObservation($user, $object) {
	
}

/* -- HELPER FUNCTIONS -- */

public static function Broadcast($ListenerList,$user,$phrase) {
	if($ListenerList===false) { return false; }
	$flag = 0;
	foreach($ListenerList as $Listener) {
		$flag = $Listener->do_execute($user,$phrase);
	}
	return $flag > 0; // At least one Listener demands that execution halt
}

public static function getRelevantListeners($user,$trigger) {
	if(!array_key_exists($trigger,StateMngr::$Listeners)) { return false; }
	
	$roomIDs		= array($user->in_room()->copy_id); // We now have everything in the room available to us
	$peopleIDs 		= array_keys($user->in_room()->people);
	$peopleIDs 		= array_keys($user->in_room()->items);
	$inventoryIDs	= array_keys($user->items); // We now have the user's inventory available to us...
	
	$KeyArray = array_merge($roomIDs,$peopleIDs,$inventoryIDs);
	
	$newA = array();
	
	foreach($KeyArray as $key) {
		if(array_key_exists($key,StateMngr::$Listeners[$trigger])) { 
			$newA[] = StateMngr::$Listeners[$trigger][$key];
		}
	}

	return $newA;
}

}

?>